#include "cframe.h"
#include "consoleplus.h"
#include <stdlib.h>
#include <string.h>
#include "log.h"
#include "keys.h"

extern Debug::DebugFile df;

namespace cio {

/* 
 * @brief : this function fills str of length m_width + 1 
 *          with border characters and fill char and appends NULL
 *          at the end
 * 1) assumption the user of the api is passing m_width + 1 char string
 */
void CFrame::setLine(char* str, char left, char fill, char right) const 
{
  
  memset(str, fill, m_width);
  str[0] = left;
  str[m_width-1] = right;
  str[m_width] = '\0';
}

/*
 * @brief captures the area hidden by the frame
 *        uses the already given function capture
 */
void CFrame::capture() 
{
  if(hidden_area)
    release(&hidden_area);

  hidden_area = cio::capture(absrow(), abscol(), m_height, m_width);
}

/*
 * @brief: calculates absolute row using parent's absrow
 *        Could not find a way otherwise and hence the function
 *        is made public
 */
int CFrame::absrow() const { 
  if(parent)
    return (parent->absrow() + position.row); 
  else 
    return 0;
}

/*
 * @brief: calculates absolute col using parent's abscol
 *        Could not find a way otherwise and hence the function
 *        is made public
 */
int CFrame::abscol() const { 
  if(parent)
    return (parent->abscol() + position.col); 
  else 
    return 0;
}

/*
 * @brief goes to the middle of the frame
 */
void CFrame::goMiddle() {
  int x = absrow();

  // this will floor the value
  if(m_width)
    x = (x + m_width) / 2;
  else
    x = (x + console.getRows() / 2);

  int y = abscol();

  if(m_height)
    y = (x + m_height) / 2;
  else
    y = (x + console.getCols() / 2);

  console.setPosition(x, y);

}

/*
 * @brief - displays the string inside the frame 
 *          based on border / no border 
 *          XXX-may require some modifications
 */
void CFrame::display(const char* str)
{
  // taking into consideration borders
  int x = hasBorder ? 1:0;
  cio::display(str, absrow() + x, abscol() + x, m_width - 2*x);
}

/*
 * @brief - edits the frame area using already developed edit function
 *          XXX - May require some modification
 */
int CFrame::edit(char* str, int fieldLen, bool* insertMode, int* offset, \
    int* cursor) 
{
  int x = hasBorder ? 1:0;
  cio::edit(str, 
      absrow() + x,
      abscol() + x,
      m_width - 2*x,
      strlen(str) + 1, 
      insertMode,
      offset,
      cursor);
}

/*
 * @brief - draws the frame with or witout border
 *  - captures the hidden areay
 *  - draws on the same area
 */
void CFrame::draw(int fn)
{
  // capture area hidden by the frame
  if(!hidden_area)
    capture();

  char str[m_width+1];
  for(int i = 0 ; i < m_height; i++) {
    char left, fill, right;
    if(hasBorder && fn == C_FULL_FRAME) { 
      if(i == 0) {
        left = border[0];
        fill = border[1];
        right = border[2];
      } else if (i == (m_height - 1)) {
        left = border[4];
        fill = border[5];
        right = border[6];
      } else {
        left = border[3];
        fill = ' ';
        right = border[7];
      }
    } else {
      left = fill = right = ' ';
    }

    // fill line
    setLine(str, left, fill, right); 

    // display
    cio::display(str, absrow() + i, abscol(), 0);
  }
}

/*
 * @brief - moves the frame in the given direction
 *  - hides the frame
 *  - moves position
 *  - draws again
 */
void CFrame::move(CDirection dir) 
{
  hide();
  
  switch(dir) {
    case C_MOVED_LEFT:
    {
      if(abscol() > 0) 
        col(position.col - 1);
    } break;
    case C_MOVED_RIGHT:
    {
      if((abscol() + m_width) < console.getCols())
        col(position.col + 1);
    } break;
    case C_MOVED_UP:
    {
      if(absrow() > 0)
        row(position.row - 1);
    } break;
    case C_MOVED_DOWN: 
    {
      if((absrow() + m_height) < console.getRows())
        row(position.row + 1);
    } break;
    case C_STATIONARY:
    default:
      {
      }
  }
  draw();
}

/*
 * @brief - hides frame and restores the original content
 */
void CFrame::hide(CDirection dir) 
{
  // restore hidden_area
  if(hidden_area) {

    // restore the things behind that
    cio::restore(absrow(), abscol(), m_height, m_width, dir, hidden_area);

    // release the hidden area
    cio::release(&hidden_area);
  }

}

/*
 * @brief - helper function for moving frames in a given direction
 */
void move(iFrame& frame) 
{
  const char* str = "Moving! ESC: exit";
  cio::display(str, 0, 0, console.getCols());

  int key;
  while(1) {
    // get key
    console >> key;

    switch(key) {
      case ESCAPE:
      {
        const char* str = " ";
        cio::display(str, 0, 0, console.getCols());
        return;
      } break;
      case UP:
      {
        frame.move(C_MOVED_UP);
      } break;
      case DOWN:
      {
        frame.move(C_MOVED_DOWN);
      } break;
      case LEFT:
      {
        frame.move(C_MOVED_LEFT);
      } break;
      case RIGHT:
      {
        frame.move(C_MOVED_RIGHT);
      } break;
      default : {
      }
    }
  }
}

} // namespace ends
